package com.lorentzos.flingswipe;

import ohos.agp.components.*;
import ohos.agp.database.DataSetSubscriber;
import ohos.agp.render.Canvas;
import ohos.agp.utils.Point;
import ohos.app.Context;
import ohos.multimodalinput.event.TouchEvent;

import java.util.ArrayList;

/**
 * Created by dionysis_lorentzos on 5/8/14
 * for package com.lorentzos.swipecards
 * and project Swipe cards.
 * Use with caution dinosaurs might appear!
 */
public class SwipeFlingAdapterView extends BaseFlingAdapterView implements ComponentContainer.ArrangeListener {
    private static final String ATTR_Y_OFFSET_STEP = "y_offset_step";

    private static final String ATTR_ROTATION_DEGREE = "rotation_degrees";

    private static final String ATTR_MIN_ADAPTER_STACK = "min_adapter_stack";

    private static final String ATTR_MAX_VISIBLE = "max_visible";

    private ArrayList<Component> cacheItems = new ArrayList<>();

    // 缩放层叠效果
    private int yOffsetStep = 92; // view叠加垂直偏移量的步长
    private static final float SCALE_STEP = 0.08f; // view叠加缩放的步长
    // 缩放层叠效果

    private int MAX_VISIBLE = 4; // 值建议最小为4
    private int MIN_ADAPTER_STACK = 6;
    private float ROTATION_DEGREES = 2f;
    private int LAST_OBJECT_IN_STACK = 0;

    private BaseItemProvider mAdapter;
    private onFlingListener mFlingListener;
    private AdapterDataSetObserver mDataSetObserver;
    private boolean mInLayout = false;
    private Component mActiveCard = null;
    private OnItemClickListener mOnItemClickListener;
    private FlingCardListener flingCardListener;
    private Point mLastTouchPoint;

    // 支持左右滑
    public boolean isNeedSwipe = true;

    private int initTop;
    private int initLeft;

    public SwipeFlingAdapterView(Context context) {
        this(context, null);
    }

    public SwipeFlingAdapterView(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    public SwipeFlingAdapterView(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        setArrangeListener(this);
        MAX_VISIBLE = AttrUtils.getIntFromAttr(attrSet, ATTR_MAX_VISIBLE, MAX_VISIBLE);
        MIN_ADAPTER_STACK = AttrUtils.getIntFromAttr(attrSet, ATTR_MIN_ADAPTER_STACK, MIN_ADAPTER_STACK);
        ROTATION_DEGREES = AttrUtils.getFloatFromAttr(attrSet, ATTR_ROTATION_DEGREE, ROTATION_DEGREES);
        yOffsetStep = AttrUtils.getDimensionFromAttr(attrSet, ATTR_Y_OFFSET_STEP, 0);
    }

    public void setIsNeedSwipe(boolean isNeedSwipe) {
        this.isNeedSwipe = isNeedSwipe;
    }

    /**
     * A shortcut method to set both the listeners and the adapter.
     *
     * @param context The activity context which extends onFlingListener, OnItemClickListener or both
     * @param mAdapter The adapter you have to set.
     */
    public void init(final Context context, BaseItemProvider mAdapter) {
        if (context instanceof onFlingListener) {
            mFlingListener = (onFlingListener) context;
        } else {
            throw new RuntimeException("Activity does not implement SwipeFlingAdapterView.onFlingListener");
        }
        if (context instanceof OnItemClickListener) {
            mOnItemClickListener = (OnItemClickListener) context;
        }
        setItemProvider(mAdapter);
    }

    @Override
    public Component getSelectedComponent() {
        return mActiveCard;
    }

    @Override
    public void postLayout() {
        if (!mInLayout) {
            super.postLayout();
        }
    }

    @Override
    public boolean onArrange(int left, int top, int right, int bottom) {
        for (int i = 0; i < getChildCount(); i++) {
            Component childComponent = getComponentAt(i);
            if (childComponent != null) {
                childComponent.setContentPositionX((getWidth() - childComponent.getWidth()) / 2f);
                adjustChildView(childComponent, i);
            }
        }
        return false;
    }

    @Override
    public void onDraw(Component component, Canvas canvas) {
        // if we don't have an adapter, we don't need to do anything
        if (mAdapter == null) {
            return;
        }
        mInLayout = true;
        final int adapterCount = mAdapter.getCount();
        Component topCard = getComponentAt(LAST_OBJECT_IN_STACK);
        if (mActiveCard != null && topCard != null && topCard == mActiveCard) {
            if (this.flingCardListener.isTouching()) {
                Point lastPoint = this.flingCardListener.getLastPoint();
                if (this.mLastTouchPoint == null || !this.mLastTouchPoint.equals(lastPoint)) {
                    this.mLastTouchPoint = lastPoint;
                    removeComponents(0, LAST_OBJECT_IN_STACK);
                    layoutChildren(1, adapterCount);
                }
            }
        } else {
            //removeAndAddToCache(0);
            layoutChildren(0, adapterCount);
            setTopView();
        }
        mInLayout = false;

        //final int adapterCount = mAdapter.getCount();
        if (adapterCount <= MIN_ADAPTER_STACK) {
            mFlingListener.onAdapterAboutToEmpty(adapterCount);
        }
    }

    private void layoutChildren(int startingIndex, int adapterCount){
        while (startingIndex < Math.min(adapterCount, MAX_VISIBLE) ) {
            Component item = null;
            if (cacheItems.size() > 0) {
                item = cacheItems.get(0);
                cacheItems.remove(item);
            }
            Component newUnderChild = mAdapter.getComponent(startingIndex, item, this);
            if (newUnderChild.getVisibility() != HIDE) {
                LAST_OBJECT_IN_STACK = startingIndex;
            }
            startingIndex++;
        }
    }

    private void adjustChildView(Component child, int index) {
        if (index > -1 && index < getMaxVisible()) {
            int multiple;
            if (index > 2) {
                multiple = 2;
            } else multiple = index;
            child.setTranslationY(yOffsetStep * multiple);
            child.setScaleX(1 - SCALE_STEP * multiple);
            child.setScaleY(1 - SCALE_STEP * multiple);
        }
    }

    private void adjustChildrenOfUnderTopView(float scrollRate) {
        int count = getChildCount();
        if (count > 1) {
            int i;
            int multiple;
            if (count == 2) {
                i = LAST_OBJECT_IN_STACK - 1;
                multiple = 0;
            } else {
                i = LAST_OBJECT_IN_STACK - 2;
                multiple = 1;
            }
            float rate = Math.abs(scrollRate);
            for (; i < LAST_OBJECT_IN_STACK; i++, multiple++) {
                Component underTopView = getComponentAt(i);
                if (underTopView != null) {
                    int offset = (int) (yOffsetStep * (multiple - rate));
                    underTopView.setTranslationY(offset - underTopView.getTop() + initTop);
                    underTopView.setScaleX(1 - SCALE_STEP * multiple + SCALE_STEP * rate);
                    underTopView.setScaleY(1 - SCALE_STEP * multiple + SCALE_STEP * rate);
                }
            }
        }
    }

    /**
     * Set the top view and add the fling listener
     */
    private void setTopView() {
        if (getChildCount() > 0) {
            mActiveCard = getComponentAt(0);
            if (mActiveCard != null) {
                flingCardListener =
                        new FlingCardListener(
                                this,
                                mActiveCard,
                                mAdapter.getItem(0),
                                ROTATION_DEGREES,
                                new FlingCardListener.FlingListener() {
                                    @Override
                                    public void onCardExited() {
                                        mActiveCard = null;
                                        mFlingListener.removeFirstObjectInAdapter();
                                    }

                                    @Override
                                    public void leftExit(Object dataObject) {
                                        mFlingListener.onLeftCardExit(dataObject);
                                    }

                                    @Override
                                    public void rightExit(Object dataObject) {
                                        mFlingListener.onRightCardExit(dataObject);
                                    }

                                    @Override
                                    public void onClick(TouchEvent event, Component component, Object dataObject) {
                                        if (mOnItemClickListener != null) {
                                            mOnItemClickListener.onItemClicked(0, dataObject);
                                        }
                                    }

                                    @Override
                                    public void onScroll(float progress, float scrollXProgress) {
                                        adjustChildrenOfUnderTopView(progress);
                                        mFlingListener.onScroll(progress);
                                    }
                                });
                mActiveCard.setTouchEventListener(flingCardListener);
            }
        }
    }

    public FlingCardListener getTopCardListener() throws NullPointerException {
        if (flingCardListener == null) {
            throw new NullPointerException();
        }
        return flingCardListener;
    }

    public void setMinStackInAdapter(int MIN_ADAPTER_STACK) {
        this.MIN_ADAPTER_STACK = MIN_ADAPTER_STACK;
    }

    /**
     * click to swipe left
     */
    public void swipeLeft() {
        getTopCardListener().selectLeft();
    }

    public void swipeLeft(int duration) {
        getTopCardListener().selectLeft(duration);
    }

    /**
     * click to swipe right
     */
    public void swipeRight() {
        getTopCardListener().selectRight();
    }

    public void swipeRight(int duration) {
        getTopCardListener().selectRight(duration);
    }

    @Override
    public BaseItemProvider getItemProvider() {
        return mAdapter;
    }

    @Override
    public void setItemProvider(BaseItemProvider provider) {
        if (mAdapter != null && mDataSetObserver != null) {
            mAdapter.removeDataSubscriber(mDataSetObserver);
            mDataSetObserver = null;
        }
        mAdapter = provider;
        if (mAdapter != null && mDataSetObserver == null) {
            mDataSetObserver = new AdapterDataSetObserver();
            mAdapter.addDataSubscriber(mDataSetObserver);
        }
        super.setItemProvider(provider);
    }

    public void setFlingListener(onFlingListener onFlingListener) {
        this.mFlingListener = onFlingListener;
    }

    public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
        this.mOnItemClickListener = onItemClickListener;
    }

    @Override
    public LayoutConfig createLayoutConfig(Context context, AttrSet attrSet) {
        return new StackLayout.LayoutConfig(context, attrSet);
    }

    private class AdapterDataSetObserver extends DataSetSubscriber {
        @Override
        public void onChanged() {
            postLayout();
        }

        @Override
        public void onInvalidated() {
            postLayout();
        }
    }
    ;

    public interface OnItemClickListener {
        void onItemClicked(int itemPosition, Object dataObject);
    }

    public interface onFlingListener {
        void removeFirstObjectInAdapter();

        void onLeftCardExit(Object dataObject);

        void onRightCardExit(Object dataObject);

        void onAdapterAboutToEmpty(int itemsInAdapter);

        void onScroll(float scrollProgressPercent);
    }
}
